import time
import cv2
import math
import numpy as np
import Processor.Converter as Converter

from PyQt5.QtWidgets import QDialog
from Plugin.blur.blur1_design import Ui_Blur1Controller
from Plugin.blur.blur2_design import Ui_Blur2Controller
from Plugin.blur.AvgBlur_design import Ui_AvgBlurController
from Plugin.blur.bilateralFilter_design import Ui_BilateralFilterController


class Controller1(QDialog, Ui_Blur1Controller):
    """
    参数为 1 的通用滤波控制器
    """
    layer = None  # 图层
    mat = None  # 原始Mat 数据

    def __init__(self, parent, mat=None):
        super().__init__(parent)
        self.setupUi(self)
        self.__initUI(parent)
        self.mat = mat

    def reject(self):
        """
        取消操作
        :return:
        """
        image = Converter.Numpy2QImage(self.mat)
        self.layer.updatePixmap(image)
        QDialog.reject(self)

    def __initUI(self, parent):
        self.setWindowTitle('滤波器')
        # 定位
        self.resize(180, 170)
        pos = parent.geometry()
        self.move((pos.width() - self.width()) / 2, (pos.height() - self.height()) / 2)
        # 信号
        #       对话框确定
        self.DialogButtonBox.accepted.connect(self.accept)
        self.DialogButtonBox.rejected.connect(self.reject)
        self.spinBox.valueChanged.connect(self.updateSlot)

    def updateSlot(self):
        value = self.spinBox.value()
        self.blur(value)

    def blur(self, value):
        """
        有子类重写
        :return:
        :param value: spinBox的值
        :return:
        """
        pass


class Controller2(QDialog, Ui_Blur2Controller):
    """
    参数为 2 的通用滤波控制器
    """
    layer = None  # 图层
    mat = None  # 原始Mat 数据

    def __init__(self, parent, mat=None):
        super().__init__(parent)
        self.setupUi(self)
        self.__initUI(parent)
        self.mat = mat

    def reject(self):
        """
        取消操作
        :return:
        """
        image = Converter.Numpy2QImage(self.mat)
        self.layer.updatePixmap(image)
        QDialog.reject(self)

    def __initUI(self, parent):
        self.setWindowTitle('滤波器')
        # 定位
        self.resize(180, 170)
        pos = parent.geometry()
        self.move((pos.width() - self.width()) / 2, (pos.height() - self.height()) / 2)
        # 信号
        #       对话框确定
        self.DialogButtonBox.accepted.connect(self.accept)
        self.DialogButtonBox.rejected.connect(self.reject)
        self.spinBox_arg1.valueChanged.connect(self.updateSlot)
        self.spinBox_arg2.valueChanged.connect(self.updateSlot)

    def updateSlot(self):
        value1 = self.spinBox_arg1.value()
        value2 = self.spinBox_arg2.value()
        self.blur(value1, value2)

    def blur(self, value1, value2):
        """
        有子类重写
        :return:
        :param value: spinBox的值
        :return:
        """
        pass


class AvgBlurController(QDialog, Ui_AvgBlurController):
    """
    均值滤波控制器
    """
    layer = None  # 图层
    mat = None  # 原始Mat 数据
    blurChangeFlag = False  # 控制titleSize spinBox数值变化 避免死循环
    blurProportion = 1  # 比例 col/row

    def __init__(self, parent, mat=None):
        super().__init__(parent)
        self.setupUi(self)
        self.initUI(parent)
        self.mat = mat

    def reject(self):
        """
        取消操作
        :return:
        """
        image = Converter.Numpy2QImage(self.mat)
        self.layer.updatePixmap(image)
        QDialog.reject(self)

    def initUI(self, parent):
        self.setWindowTitle('均值滤波')
        # 定位
        self.resize(180, 170)
        pos = parent.geometry()
        self.move((pos.width() - self.width()) / 2, (pos.height() - self.height()) / 2)
        # 信号
        #       对话框确定
        self.DialogButtonBox.accepted.connect(self.accept)
        self.DialogButtonBox.rejected.connect(self.reject)
        # 锁定长宽比
        self.radioButton_blur_fix_proportion.toggled.connect(self.radioButton_blur_fix_proportion_toggledSlot)
        # 卷积核
        self.spinBox_blur_col.valueChanged.connect(self.__blurKernelChangedSlot)
        self.spinBox_blur_row.valueChanged.connect(self.__blurKernelChangedSlot)

    def updateSlot(self):
            col = self.spinBox_blur_col.value()
            row = self.spinBox_blur_row.value()
            self.__blur(col, row)

    def radioButton_blur_fix_proportion_toggledSlot(self):
        """
        锁定blur 卷积核比率比例
        :return:
        """
        if self.radioButton_blur_fix_proportion.isChecked():
            rate = self.spinBox_blur_col.value() / self.spinBox_blur_row.value()
            if rate < 1:
                self.blurProportion = 1
            else:
                self.blurProportion = math.ceil(rate)

    def __blurKernelChangedSlot(self):
        """
        锁定卷积核 col 与 row 比例
        :return:
        """
        if self.radioButton_blur_fix_proportion.isChecked():    # 锁定卷积核比例
            if not self.blurChangeFlag:
                self.blurChangeFlag = True
                sender = self.sender()
                if sender == self.spinBox_blur_col:
                    self.spinBox_blur_row.setValue(int(self.spinBox_blur_col.value() / self.blurProportion))
                elif sender == self.spinBox_blur_row:
                    self.spinBox_blur_col.setValue(int(self.spinBox_blur_row.value() * self.blurProportion))
                self.blurChangeFlag = False
                self.updateSlot()
        else:
            self.updateSlot()

    def __blur(self, col=1, row=15):
        """

        :param col: 卷积核列数
        :param row: 卷积核行数
        :return:
        """
        if not self.layer:
            return
        start = time.perf_counter()
        mat = self.mat.copy()
        mat = cv2.blur(mat, (col, row))
        image = Converter.Numpy2QImage(mat)
        end = time.perf_counter()
        print('blur', '耗时：', end - start)
        self.layer.updatePixmap(image)


class MedianBlurController(Controller1):
    def __init__(self, parent, mat=None):
        super().__init__(parent, mat)
        self.initUI()

    def initUI(self):
        self.setWindowTitle('中值滤波')

    def blur(self, kSize):
        """
        :param kSize: 卷积核边长，采用正方形卷积核
        :return:
        """
        if not self.layer:
            return
        start = time.perf_counter()
        mat = self.mat.copy()
        mat = cv2.medianBlur(mat, kSize)
        image = Converter.Numpy2QImage(mat)
        end = time.perf_counter()
        print('medianBlur', '耗时：', end - start)
        self.layer.updatePixmap(image)


class GaussianBlurController(Controller1):
    """
    高斯滤波控制器
    """
    def __init__(self, parent, mat=None):
        super().__init__(parent, mat)
        self.initUI()

    def initUI(self):
        self.setWindowTitle('高斯滤波')
        self.spinBox.setMinimum(3)
        self.spinBox.setSingleStep(2)
        self.spinBox.setProperty("value", 5)
        self.spinBox.setMaximum(120)

    def blur(self, kSize):
        """
        :param kSize: 卷积核大小
        :return:
        """
        if not self.layer:
            return
        start = time.perf_counter()
        mat = self.mat.copy()
        mat = cv2.GaussianBlur(mat, ksize=(kSize, kSize), sigmaX=0, sigmaY=0)
        image = Converter.Numpy2QImage(mat)
        end = time.perf_counter()
        print('GaussianBlur', '耗时：', end - start)
        self.layer.updatePixmap(image)


class HighPassBlurController(Controller1):
    """
    高反差保留滤波控制器
    dst = 原图 - 高斯滤波 + 128
    """
    def __init__(self, parent, mat=None):
        super().__init__(parent, mat)
        self.initUI()

    def initUI(self):
        self.setWindowTitle('高反差保留')
        self.spinBox.setMinimum(3)
        self.spinBox.setSingleStep(2)
        self.spinBox.setProperty("value", 7)
        self.spinBox.setMaximum(120)

    def blur(self, kSize):
        """
        :param kSize: 卷积核大小
        :return:
        """
        if not self.layer:
            return
        start = time.perf_counter()
        mat = self.mat.copy()
        data = mat[..., 0:3]
        dst = cv2.GaussianBlur(data, ksize=(kSize, kSize), sigmaX=0, sigmaY=0)
        dst = np.int16(data) - np.int16(dst) + 128
        mat[..., 0:3] = np.uint8(np.clip(dst, 0, 255))
        image = Converter.Numpy2QImage(mat)
        end = time.perf_counter()
        print('HighPassBlur', '耗时：', end - start)
        self.layer.updatePixmap(image)


class BilateralFilterController(QDialog, Ui_BilateralFilterController):
    layer = None  # 图层
    mat = None  # 原始Mat 数据

    def __init__(self, parent, mat=None):
        super().__init__(parent)
        self.setupUi(self)
        self.initUI(parent)
        self.mat = mat

    def initUI(self, parent):
        self.setWindowTitle('双边滤波')
        # 定位
        self.resize(300, 170)
        pos = parent.geometry()
        self.move((pos.width() - self.width()) / 2, (pos.height() - self.height()) / 2)
        # 信号
        #       对话框确定
        self.DialogButtonBox.accepted.connect(self.accept)
        self.DialogButtonBox.rejected.connect(self.reject)
        self.horizontalSlider_kSize.valueChanged.connect(self.updateSlot)
        self.horizontalSlider_sigmaSpace.valueChanged.connect(self.updateSlot)
        self.horizontalSlider_sigmaColor.valueChanged.connect(self.updateSlot)

    def reject(self):
        """
        取消操作
        :return:
        """
        image = Converter.Numpy2QImage(self.mat)
        self.layer.updatePixmap(image)
        QDialog.reject(self)

    def updateSlot(self):
        kSize = self.horizontalSlider_kSize.value()
        sigmaColor = self.horizontalSlider_sigmaColor.value()
        sigmaSpace = self.horizontalSlider_sigmaSpace.value()
        self.label_kSize.setText(str(kSize))
        self.label_sigmaColor.setText(str(sigmaColor))
        self.label_sigmaSpace.setText(str(sigmaSpace))
        self.blur(kSize, sigmaColor, sigmaSpace)

    def blur(self, kSize=0, sigmaColor=45, sigmaSpace=45):
        """
        双边滤波
        其中核大小通常为sigma的6*sigma + 1。
        因为离中心点3*sigma大小之外的系数与中点的系数只比非常小，
        可以认为此之外的点与中心点没有任何联系，及权重系数为0
        :param kSize: 卷积核大小
        :param sigmaColor: 灰度域标准差
        :param sigmaSpace: 空间域标准差, 越大，边界越模糊
        :return:
        """
        if not self.layer:
            return
        start = time.perf_counter()
        mat = self.mat.copy()
        mat[..., 0:3]= cv2.bilateralFilter(mat[..., 0:3], kSize, sigmaColor, sigmaSpace)
        image = Converter.Numpy2QImage(mat)
        end = time.perf_counter()
        print('BilateralFilter', '耗时：', end - start)
        self.layer.updatePixmap(image)


class SharpnessController(Controller1):
    def __init__(self, parent, mat=None):
        super().__init__(parent, mat)
        self.initUI()

    def initUI(self):
        self.setWindowTitle('图像锐化')

    def blur(self, kSize):
        """
        :param kSize: 卷积核边长，采用正方形卷积核
        :return:
        """
        if not self.layer:
            return
        start = time.perf_counter()
        mat = self.mat.copy()
        kernel = np.array((
            [-2, -1, 0],
            [-1, 1, 1],
            [0, 1, 2]), dtype="float32")

        dst = cv2.filter2D(mat, -1, kernel)
        image = Converter.Numpy2QImage(dst)
        end = time.perf_counter()
        print('SharpnessController', '耗时：', end - start)
        self.layer.updatePixmap(image)


class CannyController(Controller2):
    def __init__(self, parent, mat=None):
        super().__init__(parent, mat)
        self.initUI()

    def initUI(self):
        self.setWindowTitle('边缘检测')

    def blur(self, thre1, thre2):
        if not self.layer:
            return
        start = time.perf_counter()
        mat = self.mat.copy()
        mat = cv2.Canny(mat, thre1, thre2)
        mat = cv2.cvtColor(mat, cv2.COLOR_GRAY2RGBA)
        image = Converter.Numpy2QImage(mat)
        end = time.perf_counter()
        print('Canny', '耗时：', end - start)
        self.layer.updatePixmap(image)

